#include <time.h>
#include <winsock2.h>
#include "MySQL/mysql.h"
#include "pThreads/pthread.h"
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"

#include "CSpawnPointHandler.h"
#include "CNpcPacket.h"
#include "CNpc.h"
#include "CPacket.h"
#include "CConsole.h"
#include "CUtil.h"
#include "CDb.h"

/*std::vector<struct spawnpoint> CSpawnPointHandler::spawnPointList[256];

CSemaphore* CSpawnPointHandler::spawnPointListSemaphore[256];

CSpawnPointHandler::CSpawnPointHandler(void)
{
}

CSpawnPointHandler::~CSpawnPointHandler(void)
{
}

void CSpawnPointHandler::initSpawnPointSemaphor(int zone)
{
	if (zone>255)
	{
		CConsole::outErr("Tried to init semaphore for Zone > 255");
	}

	//enable access to spawnPointList
	CSpawnPointHandler::spawnPointListSemaphore[zone] = new CSemaphore(1);
}

void CSpawnPointHandler::lockSpawnPointList(int zone)
{
	CSpawnPointHandler::spawnPointListSemaphore[zone]->lock();
}

void CSpawnPointHandler::unlockSpawnPointList(int zone)
{
	CSpawnPointHandler::spawnPointListSemaphore[zone]->unlock();
}

void CSpawnPointHandler::addNode(CChar * schar, int spawnId)
{
	MYSQL* mysqlConn;
	int count;
	char query[4096];

	roamnode newRoamNode;
	newRoamNode.rotation = schar->loc.p.rotation;
	newRoamNode.x = schar->loc.p.x;
	newRoamNode.y = schar->loc.p.y;
	newRoamNode.z = schar->loc.p.z;

	CSpawnPointHandler::despawnNodes(schar, spawnId);

	// Update database nodes
	CSpawnPointHandler::lockSpawnPointList(schar->getZone());
	for(int i = CSpawnPointHandler::getNodeCount(spawnId, (unsigned char)schar->getZone()); i >= schar->targNode+1; i--) {
		//sql update nodeid to nodeid+1 for nodes after targetted node
		memset(query,0,4096);
		sprintf(query, "UPDATE npc_%s_nodes SET nodeid=%i WHERE nodeid=%i AND spawnid=%i", CUtil::getZoneName(schar->getZone()), i+1, i, spawnId+1);

		count = (int) strlen(query);

		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn
	}
	memset(query,0,4096);
	sprintf(query, "INSERT INTO npc_%s_nodes (`spawnid`, `nodeid`, `pos_rot`, `pos_x`, `pos_y`, `pos_z`, `nodetype`) VALUES (%i,%i,%i,%.3f,%.3f,%.3f,'spawn')",
		CUtil::getZoneName(schar->getZone()), spawnId+1, schar->targNode+1, newRoamNode.rotation, newRoamNode.x, newRoamNode.y, newRoamNode.z);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	// add node to memory
	if(schar->targNode == CSpawnPointHandler::getNodeCount(spawnId, schar->loc.zone)) {
		CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.push_back(newRoamNode);
	} else {
		CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.insert( CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.begin()+schar->targNode, newRoamNode );
	}
	CSpawnPointHandler::unlockSpawnPointList(schar->getZone());
}

void CSpawnPointHandler::deleteNode(CChar * schar, int spawnId)
{
	MYSQL* mysqlConn;
	int count;
	char query[4096];

	// Despawn nodes
	CSpawnPointHandler::despawnNodes(schar, spawnId);
	// Update database nodes
	memset(query,0,4096);
	sprintf(query, "DELETE FROM npc_%s_nodes WHERE spawnid=%i AND nodeid=%i", CUtil::getZoneName(schar->getZone()), spawnId+1, schar->targNode);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	CSpawnPointHandler::lockSpawnPointList(schar->getZone());
	for(int i = schar->targNode+1; i <= CSpawnPointHandler::getNodeCount(spawnId, (unsigned char)schar->getZone()); i++) {
		//sql update nodeid to nodeid-1 for nodes after targetted node
		memset(query,0,4096);
		sprintf(query, "UPDATE npc_%s_nodes SET nodeid=%i WHERE nodeid=%i AND spawnid=%i", CUtil::getZoneName(schar->getZone()), i-1, i, spawnId+1);

		count = (int) strlen(query);

		MYSQL* mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn
	}

	// Delete node from memory
	CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.erase( CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.begin()+schar->targNode-1 );
	CSpawnPointHandler::unlockSpawnPointList(schar->getZone());
}

void CSpawnPointHandler::deleteAllNodes(CChar * schar, int spawnId)
{
	MYSQL* mysqlConn;
	int count;
	char query[4096];

	// Despawn nodes
	CSpawnPointHandler::despawnNodes(schar, spawnId);

	CSpawnPointHandler::lockSpawnPointList(schar->getZone());
	for(int i = 2; i <= CSpawnPointHandler::getNodeCount(spawnId, (unsigned char)schar->getZone()); i++) {
		//sql delete nodes 2 to node count
		memset(query,0,4096);
		sprintf(query, "DELETE FROM npc_%s_nodes WHERE spawnid=%i AND nodeid=%i", CUtil::getZoneName(schar->getZone()), spawnId+1, i);
		count = (int) strlen(query);

		mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn

		// Delete node from memory
		CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.erase( CSpawnPointHandler::spawnPointList[schar->getZone()].at(spawnId).nodeList.begin()+1 );
	}
	CSpawnPointHandler::unlockSpawnPointList(schar->getZone());
}

unsigned short CSpawnPointHandler::getNodeCount(unsigned short spawnPoint, unsigned char zone)
{
	CSpawnPointHandler::lockSpawnPointList(zone);
	unsigned short nodeCNT = 0;
	if (CSpawnPointHandler::spawnPointList[zone].size() > spawnPoint) {
		nodeCNT = (unsigned short)CSpawnPointHandler::spawnPointList[zone].at(spawnPoint).nodeList.size();
	}
	CSpawnPointHandler::unlockSpawnPointList(zone);
	return nodeCNT;
}

void CSpawnPointHandler::spawnNodes(CChar * schar, int spawnId)
{
	CSpawnPointHandler::lockSpawnPointList(schar->getZone());
	for(int i = 1; i <= CSpawnPointHandler::getNodeCount(spawnId, (unsigned char)schar->getZone()); i++){
		CNpc nodeNpc;

		nodeNpc.m_id				= (spawnId+1)*100 + i;
		sprintf(nodeNpc.m_name, "S%iN%i", spawnId+1, i);
		nodeNpc.type				= 0x07;
		nodeNpc.loc.zone			= schar->loc.zone;
		nodeNpc.setTargId			(0x800 - i);
		nodeNpc.loc.p				= CSpawnPointHandler::getNodePosition(spawnId, i-1, (unsigned char)schar->getZone());
		nodeNpc.loc.p.x				= (float)(nodeNpc.getXPos() + 0.010);  // Offset slightly from mob so char can target
		nodeNpc.targ_p				= nodeNpc.getPos();
		nodeNpc.status				= 0;
		nodeNpc.look				= 0x03310000;  //Beastmen banner
		nodeNpc.unknown				= 0x19;
		nodeNpc.m_speed				= 0x38;
		nodeNpc.m_speedAlt			= 0x38;
		nodeNpc.h.hp				= 100;
		nodeNpc.m_maxHp				= 100;
		schar->quePacket(CNpcPacket(&nodeNpc));
		//CConsole::outDebOnly("spawnNodes %i %s %i",nodeNpc.m_id, nodeNpc.m_name, nodeNpc.m_targId);  //DEBUG
	}
	CSpawnPointHandler::unlockSpawnPointList((unsigned char)schar->getZone());
}

void CSpawnPointHandler::despawnNodes(CChar * schar, int spawnId)
{
	CSpawnPointHandler::lockSpawnPointList(schar->getZone());
	for(unsigned short i = 1; i <= CSpawnPointHandler::getNodeCount(spawnId, (unsigned char)schar->getZone()); i++){
		CNpc nodeNpc;

		nodeNpc.m_id				= (spawnId+1)*100 + i;
		nodeNpc.type				= 0x07;
		nodeNpc.loc.zone			= (unsigned char)schar->getZone();
		nodeNpc.setTargId(0x800 - i);
		nodeNpc.loc.p				= CSpawnPointHandler::getNodePosition(spawnId, i-1, (unsigned char)schar->getZone());
		nodeNpc.targ_p				= nodeNpc.loc.p;
		nodeNpc.status				= 3;
		schar->quePacket(CNpcPacket(&nodeNpc));
	}
	CSpawnPointHandler::unlockSpawnPointList((unsigned char)schar->getZone());
}

spawnpoint * CSpawnPointHandler::getSpawnPoint(unsigned short spawnId, unsigned char zone) 
{
	return &CSpawnPointHandler::spawnPointList[zone].at(spawnId);
}

position CSpawnPointHandler::getNodePosition(unsigned short spawnId, unsigned short node, unsigned char zone) {
	position pos;

	spawnpoint * spawnPoint;
	spawnPoint = CSpawnPointHandler::getSpawnPoint(spawnId, zone);

	pos.rotation = spawnPoint->nodeList.at(node).rotation;
	pos.x = spawnPoint->nodeList.at(node).x;
	pos.y = spawnPoint->nodeList.at(node).y;
	pos.z = spawnPoint->nodeList.at(node).z;

	return pos;

}

DWORD WINAPI SpawnPointServ(LPVOID lpParam) {
	
	CChar * schar = (CChar*)lpParam;
	CNpc * target = (CNpc*)schar->m_pTarget;
	int spawnPoint = target->spawnId;

	while(schar->seeNodes)
	{
		if (schar->loggedOut)
		{
			schar->seeNodes=false;
			break;
		}

		if (spawnPoint<0)
		{
			CConsole::outErr("Spawnpoint<0");
			schar->seeNodes=false;
			break;
		}

		switch(schar->nodeAction){
			case 0:
				//Do nothing to a node
				break;
			case 1:
				//Add node
				if(CSpawnPointHandler::getNodeCount(spawnPoint, (unsigned char)schar->getZone()) < 256) {
					CConsole::outDebOnly("Target node %i",schar->targNode);
					// Reset MOB or NPC to node 0
					target->update = FALSE;
					target->atNode = 0;
					target->walkNumNodes = 0;
					target->loc.p = CSpawnPointHandler::getNodePosition(target->spawnId, target->atNode, (unsigned char)target->getZone());
					target->sendUpdate();
					CSpawnPointHandler::addNode(schar, spawnPoint);
					schar->nodeAction = 10;
				} else {
					char msg[255];
					memset(msg,0,255);
					sprintf(msg,"DELNODE: Failed. 256 nodes reached for this MOB or NPC.");
					CPacket::sendServerTell("Axium",  schar->m_name, msg);
					schar->nodeAction = 0;
				}
				break;
			case 2:
				//Delete node
				if(CSpawnPointHandler::getNodeCount(spawnPoint, (unsigned char)schar->getZone()) > 1) {
					CConsole::outDebOnly("Target node %i",schar->targNode);
					// Reset MOB or NPC to node 0
					target->update = FALSE;
					target->atNode = 0;
					target->walkNumNodes = 0;
					target->loc.p = CSpawnPointHandler::getNodePosition(target->spawnId, target->atNode, (unsigned char)target->getZone());
					target->sendUpdate();
					CSpawnPointHandler::deleteNode(schar, spawnPoint);
					schar->nodeAction = 10;
				} else {
					char msg[255];
					memset(msg,0,255);
					sprintf(msg,"DELNODE: Failed. Cannot delete the last node.");
					CPacket::sendServerTell("Axium",  schar->m_name, msg);
					schar->nodeAction = 0;
				}
				break;
			case 3:
				//Delete all nodes
				if(CSpawnPointHandler::getNodeCount(spawnPoint, (unsigned char)schar->getZone()) > 1) {
					// Reset MOB or NPC to node 0
					target->update = FALSE;
					target->atNode = 0;
					target->walkNumNodes = 0;
					target->loc.p = CSpawnPointHandler::getNodePosition(target->spawnId, target->atNode, (unsigned char)target->getZone());
					target->sendUpdate();
					CSpawnPointHandler::deleteAllNodes(schar, spawnPoint);
					schar->nodeAction = 10;
				} else {
					CPacket::sendServerTell("Axium", schar->m_name, "DELALLNODES: Failed. MOB/NPC has only 1 node");
					schar->nodeAction = 0;
				}
				break;
			case 10:
				//Spawn nodes
				schar->nodeAction = 0;
				CSpawnPointHandler::spawnNodes(schar, spawnPoint);
				break;
		}
		Sleep(1000);
	}
	//Despawn nodes
	CSpawnPointHandler::despawnNodes(schar, spawnPoint);
	return 0;
}
*/